﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Magiq.Support {
  public static class Reflection {
    private static readonly ReflectionBuilder Builder = new ReflectionBuilder();

    private static readonly Dictionary<string, Func<object, object>> FieldGetters =
      new Dictionary<string, Func<object, object>>();

    private static readonly Dictionary<string, Func<object, object>> PropertyGetters =
      new Dictionary<string, Func<object, object>>();

    private static readonly Dictionary<string, Action<object, object>> PropertySetters =
      new Dictionary<string, Action<object, object>>();

    public static T Field<T>(this object self, string field) {
      return (T) Field(self, field);
    }

    private static string Key(Type type, string member) {
      return type.FullName + "." + member;
    }

    public static object Field(this object self, string field) {
      var key = Key(self.GetType(), field);
      Func<object, object> getter;

      if (!FieldGetters.TryGetValue(key, out getter)) {
        getter = Builder.FieldGetter(self.GetType(), field);
        FieldGetters.Add(key, getter);
      }
      return getter(self);
    }

    public static object Property(this object self, string property) {
      var key = Key(self.GetType(), property);
      Func<object, object> getter;

      if (!PropertyGetters.TryGetValue(key, out getter)) {
        getter = Builder.PropertyGetter(self.GetType(), property);
        PropertyGetters.Add(key, getter);
      }
      return getter(self);
    }

    public static T Property<T>(this object self, string property) {
      return (T) self.Property(property);
    }

    public static void SetProperty(this object self, string property, object value) {
      self.GetType().PropertySetter(property)(self, value);
    }

    public static Action<object, object> PropertySetter(this Type self, string property) {
      Action<object, object> setter;
      var key = Key(self, property);
      if (!PropertySetters.TryGetValue(key, out setter)) {
        setter = Builder.PropertySetter(self, property);
        PropertySetters.Add(key, setter);
      }
      return setter;
    }

    public static bool HasProperty(this object self, string property) {
      return self.GetType().GetProperty(property) != null;
    }
  }
}